Skip to content

JS 函数缓存和使用场景

JS 函数缓存

shell
# 什么是函数缓存
函数的本质是将函数的计算结果临时缓存起来,当后续以相同参数调用该函数时,直接返回缓存的结果,而非重新执行函数逻辑。

# 实现函数缓存
函数缓存的核心是缓存参数与结果的映射,通过「空间换时间」减少重复计算 / 请求,提升性能;

实现函数缓存

实现函数缓存主要依靠闭包、柯里化、高阶函数

js
/**
 * 1. 利用函数闭包特性,定义缓存容器
 * 2. 将函数参数拼接成唯一的键
 * 3. 函数调用时,检查缓存中是否存在该键,存在则直接返回结果,不存在则执行函数,将结果存入缓存容器后返回。
 * 优化:可使用 Map 结构作为缓存容器
 */

// 缓存函数封装
function createCachedFunction(fn) {
  // 缓存容器(用对象存储键值对)
  const cache = {};

  // 返回包装后的函数
  return function (...args) {
    // 生成唯一的缓存键(参数拼接,简单场景可用)
    const cacheKey = JSON.stringify(args);

    // 检查缓存:存在则直接返回
    if (cacheKey in cache) {
      console.log(`命中缓存:参数 ${cacheKey}`);
      return cache[cacheKey];
    }

    // 未命中则执行原函数,并存入缓存
    console.log(`未命中缓存:参数 ${cacheKey}`);
    const result = fn.apply(this, args);
    cache[cacheKey] = result;

    return result;
  };
}

// 测试用例:模拟耗时计算函数
function calculateSum(a, b) {
  // 模拟耗时操作(比如复杂计算)
  console.log('执行了耗时计算');
  return a + b;
}

// 创建带缓存的函数
const cachedCalculate = createCachedFunction(calculateSum);

// 第一次调用:未命中,执行计算
console.log(cachedCalculate(1, 2)); // 输出:执行了耗时计算 → 3
// 第二次调用:命中缓存,直接返回
console.log(cachedCalculate(1, 2)); // 输出:命中缓存 → 3
// 不同参数:未命中
console.log(cachedCalculate(3, 4)); // 输出:执行了耗时计算 → 7

函数缓存使用场景

shell
1. 计算密集型函数:函数包含大量循环、数学计算、递归(如斐波那契数列、排序算法),且参数重复调用概率高。
2. 网络请求 / 异步数据获取:前端请求后端接口(如列表数据、详情数据),相同参数的请求无需重复发送(减少网络开销和服务器压力)。
3. 配置 / 静态数据读取:函数读取固定配置(如根据环境 / 用户角色返回不同配置),配置不会频繁变更,缓存可减少 IO / 解析开销。

拓展

shell
# 函数缓存限制
如果函数依赖外部变量(如全局变量、时间)或有副作用(如修改 DOM),则不适合缓存;

# 优化
长期运行的程序需注意缓存清理,避免缓存无限增长占用内存。

函数缓存(另一稿)

函数缓存是一种优化技术,通过存储函数计算结果来避免重复计算,从而提高性能(目的是优化:利用空间换时间)

如何实现函数缓存

函数闭包、函数柯里化、高阶函数

函数闭包实现缓存

对象结构存储缓存,Map 结构结构存储缓存

ts
  // 对象结构存储缓存
  function memoize(fn) {
    const cache = {};
    console.log('cache',cache)
    return function(...args) {
      const key = JSON.stringify(args);
      if (key in cache) {
        return cache[key];
      }
      const result = fn.apply(this, args);
      cache[key] = result;
      console.log('cache',cache)
      return result;
    };
  }
    // Map 结构结构存储缓存
    // function memoize(fn) {
    //   const cache = new Map();
    //   return function(...args) {
    //     const key = JSON.stringify(args);
    //     if (cache.has(key)) {
    //       return cache.get(key);
    //     }
    //     const result = fn.apply(this, args);
    //     cache.set(key, result);
    //     return result;
    //   };
    // }

  // 使用示例
  function expensiveCalculation(n) {
    console.log('Calculating...');
    return n * 2;
  }

  const memoizedCalc = memoize(expensiveCalculation);

  console.log(memoizedCalc(5)); // 计算并缓存
  console.log(memoizedCalc(5)); // 从缓存读取
  console.log(memoizedCalc(6)); // 计算并缓存
  console.log(memoizedCalc(6)); // 从缓存读取
  1. 带过期时间的缓存
ts
  function memoizeWithExpiry(fn, expiryTime) {
    const cache = {};
    return function(...args) {
      const key = JSON.stringify(args);
      const now = Date.now();

      if (cache[key] && now < cache[key].expiry) {
        return cache[key].value;
      }

      const result = fn.apply(this, args);
      cache[key] = {
        value: result,
        expiry: now + expiryTime
      };
      return result;
    };
  }

  // 使用示例
  function expensiveCalculation(n) {
    console.log('Calculating...');
    return n + 2;
  }

  const memoizedCalc = memoizeWithExpiry(expensiveCalculation,5000); // 缓存 5000 ms

  console.log(memoizedCalc(5)); // 计算并缓存
  console.log(memoizedCalc(5)); // 从缓存读取
  console.log(memoizedCalc(6)); // 计算并缓存
  console.log(memoizedCalc(6)); // 从缓存读取